#ifndef DW_MATHS_MATRIX_H
#	define DW_MATHS_MATRIX_H

namespace dw {
	namespace maths {

template< typename T, unsigned N >
struct Vector;

template< typename T, unsigned M, unsigned N >
struct Matrix;

template< typename T, unsigned M, unsigned N >
inline bool operator == (const Matrix< T, M, N >& lhs, const Matrix< T, M, N >& m);

template< typename T, unsigned M, unsigned N >
inline bool operator != (const Matrix< T, M, N >& lhs, const Matrix< T, M, N >& rhs);

template< typename T, unsigned M, unsigned N >
inline Matrix< T, M, N > operator + (const Matrix< T, M, N >& lhs, const Matrix< T, M, N >& rhs);

template< typename T, unsigned M, unsigned N >
inline Matrix< T, M, N > operator - (const Matrix< T, M, N >& rhs);

template< typename T, unsigned M, unsigned N >
inline Matrix< T, M, N > operator - (const Matrix< T, M, N >& lhs, const Matrix< T, M, N >& rhs);

template< typename T, unsigned M, unsigned N, unsigned P >
inline Matrix< T, M, P > operator * (const Matrix< T, M, N >& lhs, const Matrix< T, N, P >& rhs);

template< typename T >
inline Matrix< T, 4, 4 > operator * (const Matrix< T, 4, 4 >& lhs, const Matrix< T, 4, 4 >& rhs);

template< typename T >
inline Matrix< T, 3, 3 > mul(const Matrix< T, 3, 3 >& lhs, const Matrix< T, 3, 3 >& rhs);

// M = rows = j
// N = columns = i
template< typename T, unsigned M, unsigned N >
struct Matrix
{
public : //! @name Constants
	//! @{
	static const Matrix< T, M, N > zero;
	//! @}

public : //! @name Data
	//! @{
	union
	{
		struct
		{
			Vector< T, N > v[M];
		};
		T m[M * N];
	};
	//! @}

public : //! @name Construction & Destruction
	//! @{
	inline Matrix();
	inline Matrix(const T* m);
	//! @}

// Methods
public :
	inline const T& operator[](const unsigned i) const ;
	inline const T& operator()(const unsigned i, const unsigned j) const ;
	inline T& operator[](const unsigned i);
	inline T& operator()(const unsigned i, const unsigned j);
	inline Matrix< T, M, N >& operator = (const Matrix< T, M, N >& rhs);
	inline Matrix< T, M, N >& operator += (const Matrix< T, M, N >& rhs);
	inline Matrix< T, M, N >& operator -= (const Matrix< T, M, N >& rhs);
	inline Matrix< T, M, N >& operator *= (const T& rhs);
	inline Matrix< T, M, N >& operator /= (const T& rhs);
	inline Matrix< T, M, N >& reset();
	const Vector< T, N >& getRow(const unsigned i) const ;
};

// Matrix< T, N, N > partial specialization /////////////////////////////////

template< typename T, unsigned N >
struct Matrix< T, N, N >
{
public : //! @name Constants
	//! @{
	static const Matrix< T, N, N > zero;
	static const Matrix< T, N, N > identity;
	//! @}

public : //! @name Data
	//! @{
	union
	{
		struct
		{
			Vector< T, N > v[N];
		};
		T m[N * N];
	};
	//! @}

public : //! @name Construction & Destruction
	//! @{
	inline Matrix();
	inline Matrix(const T* m);
	//! @}

// Methods
public :
	inline const T& operator[](const unsigned i) const ;
	inline const T& operator()(const unsigned i, const unsigned j) const ;
	inline T& operator[](const unsigned i);
	inline T& operator()(const unsigned i, const unsigned j);
	inline Matrix< T, N, N >& operator=(const Matrix< T, N, N >& rhs);
	inline Matrix< T, N, N >& operator+=(const Matrix< T, N, N >& rhs);
	inline Matrix< T, N, N >& operator-=(const Matrix< T, N, N >& rhs);
	inline Matrix< T, N, N >& operator*=(const T& rhs);
	inline Matrix< T, N, N >& operator/=(const T& rhs);

	inline Matrix< T, N, N >& reset();
	inline const Vector< T, N >& getRow(const unsigned i) const ;
	inline std::string toString() const ;

	static T det(const Matrix< T, N, N >& m);
	static T trace(const Matrix< T, N, N >& m);
	static Matrix< T, N, N > transpose(const Matrix< T, N, N >& m);
	static Matrix< T, N, N > cofactor(const Matrix< T, N, N >& m);
	static Matrix< T, N, N > adjoint(const Matrix< T, N, N >& m);
	static Matrix< T, N, N > inverse(const Matrix< T, N, N >& m);
};


// Matrix< T, 2, 2 > partial specialization /////////////////////////////////

template< typename T >
struct Matrix< T, 2, 2 >
{
public : //! @name Constants
	//! @{
	static const Matrix< T, 2, 2 > zero;
	static const Matrix< T, 2, 2 > identity;
	//! @}

public : //! @name Data
	//! @{
	union
	{
		struct
		{
			Vector< T, 2 > v[2];
		};
		T m[2 * 2];
	};
	//! @}

public : //! @name Construction & Destruction
	//! @{
	inline Matrix();
	inline Matrix(const T* m);
	inline Matrix(const T& m00, const T& m01, const T& m10, const T& m11);
	inline Matrix(const Vector< T, 2 >& v0, const Vector< T, 2 >& v1);
	//! @}

// Methods
public :
	inline const T& operator[](const unsigned i) const ;
	inline const T& operator()(const unsigned i, const unsigned j) const ;
	inline T& operator[](const unsigned i);
	inline T& operator()(const unsigned i, const unsigned j);
	inline Matrix< T, 2, 2 >& operator=(const Matrix< T, 2, 2 >& rhs);
	inline Matrix< T, 2, 2 >& operator+=(const Matrix< T, 2, 2 >& rhs);
	inline Matrix< T, 2, 2 >& operator-=(const Matrix< T, 2, 2 >& rhs);
	inline Matrix< T, 2, 2 >& operator*=(const T& rhs);
	inline Matrix< T, 2, 2 >& operator/=(const T& rhs);

	inline Matrix< T, 2, 2 >& reset();
	inline const Vector< T, 2 >& getRow(const unsigned i) const ;
	inline std::string toString() const ;

	static T det(const Matrix< T, 2, 2 >& m);
	static T trace(const Matrix< T, 2, 2 >& m);
	static Matrix< T, 2, 2 > transpose(const Matrix< T, 2, 2 >& m);
	static Matrix< T, 2, 2 > cofactor(const Matrix< T, 2, 2 >& m);
	static Matrix< T, 2, 2 > adjoint(const Matrix< T, 2, 2 >& m);
	static Matrix< T, 2, 2 > inverse(const Matrix< T, 2, 2 >& m);
	static Matrix< T, 2, 2 > rotation(const Vector< T, 2 >& axis, const T& angle);
	static Matrix< T, 2, 2 > scaling(const T& sx, const T& sy);
	static Matrix< T, 2, 2 > scaling(const Vector< T, 2 >& scale);
};

// Matrix< T, 3, 3 > partial specialization /////////////////////////////////

template< typename T >
struct Matrix< T, 3, 3 >
{
public : //! @name Constants
	//! @{
	static const Matrix< T, 3, 3 > zero;
	static const Matrix< T, 3, 3 > identity;
	//! @}

public : //! @name Data
	//! @{
	union
	{
		struct
		{
			Vector< T, 3 > right;
			Vector< T, 3 > up;
			Vector< T, 3 > at;
		};
		T m[3 * 3];
	};
	//! @}

public : //! @name Construction & Destruction
	//! @{
	inline Matrix();
	inline Matrix(const T* m);
	inline Matrix(
		const T& m00, const T& m01, const T& m02, 
		const T& m10, const T& m11, const T& m12, 
		const T& m20, const T& m21, const T& m22);
	inline Matrix(const Vector< T, 3 >& v0, const Vector< T, 3 >& v1, const Vector< T, 3 >& v2);
	//! @}

// Methods
public :
	inline const T& operator[](const unsigned i) const ;
	inline const T& operator()(const unsigned i, const unsigned j) const ;
	inline T& operator[](const unsigned i);
	inline T& operator()(const unsigned i, const unsigned j);
	inline Matrix< T, 3, 3 >& operator=(const Matrix< T, 3, 3 >& rhs);
	inline Matrix< T, 3, 3 >& operator+=(const Matrix< T, 3, 3 >& rhs);
	inline Matrix< T, 3, 3 >& operator-=(const Matrix< T, 3, 3 >& rhs);
	inline Matrix< T, 3, 3 >& operator*=(const T& rhs);
	inline Matrix< T, 3, 3 >& operator/=(const T& rhs);

	inline Matrix< T, 3, 3 >& reset();
	inline const Vector< T, 3 >& getRow(const unsigned i) const ;
	inline std::string toString() const ;

	static T det(const Matrix< T, 3, 3 >& m);
	static T trace(const Matrix< T, 3, 3 >& m);
	static Matrix< T, 3, 3 > transpose(const Matrix< T, 3, 3 >& m);
	static Matrix< T, 3, 3 > cofactor(const Matrix< T, 3, 3 >& m);
	static Matrix< T, 3, 3 > adjoint(const Matrix< T, 3, 3 >& m);
	static Matrix< T, 3, 3 > inverse(const Matrix< T, 3, 3 >& m);
	static Matrix< T, 3, 3 > rotation(const T& yaw, const T& pitch, const T& roll);
	static Matrix< T, 3, 3 > rotation(const Vector< T, 3 >& axis, const T& angle);
	static Matrix< T, 3, 3 > scaling(const T& sx, const T& sy, const T& sz);
	static Matrix< T, 3, 3 > scaling(const Vector< T, 3 >& scale);
};

//==============================================================================
// Matrix< 4, 4, T >
//==============================================================================

template< typename T >
struct Matrix< T, 4, 4 >
{
public : //! @name Constants
	//! @{
	static const Matrix< T, 4, 4 > zero;
	static const Matrix< T, 4, 4 > identity;
	//! @}

public : //! @name Data
	//! @{
	union
	{
		struct
		{
			Vector< T, 4 > right;
			Vector< T, 4 > up;
			Vector< T, 4 > at;
			Vector< T, 4 > pos;
		};
		T m[4 * 4];
	};
	//! @}

public : //! @name Construction & Destruction
	//! @{
	// Matrix();
	Matrix(const Matrix< T, 4, 4 >& ref = identity);
	Matrix(const Vector< T, 4 >& pRight, const Vector< T, 4 >& pUp, const Vector< T, 4 >& pAt, const Vector< T, 4 >& pPos);
	Matrix(const T* m);
	Matrix(const T& m00, const T& m01, const T& m02, const T&m03,
		const T& m10, const T& m11, const T& m12, const T&m13,
		const T& m20, const T& m21, const T& m22, const T&m23,
		const T& m30, const T& m31, const T& m32, const T&m33);
	Matrix(const Matrix< T, 3, 3 >& rot, const Vector< T, 3 >& pPos);
	//! @}

public :
	inline const T& operator[](const unsigned i) const ;
	inline const T& operator()(const unsigned i, const unsigned j) const ;
	inline T& operator[](const unsigned i);
	inline T& operator()(const unsigned i, const unsigned j);
	inline Matrix< T, 4, 4 >& operator=(const Matrix< T, 4, 4 >& rhs);
	inline Matrix< T, 4, 4 >& operator+=(const Matrix< T, 4, 4 >& rhs);
	inline Matrix< T, 4, 4 >& operator-=(const Matrix< T, 4, 4 >& rhs);
	inline Matrix< T, 4, 4 >& operator*=(const T& rhs);
	inline Matrix< T, 4, 4 >& operator/=(const T& rhs);

	inline Matrix< T, 4, 4 >& reset();
	inline const Vector< T, 4 >& getRow(const unsigned i) const ;
	inline std::string toString() const ;

	static T det(const Matrix< T, 4, 4 >& m);
	static T trace(const Matrix< T, 4, 4 >& m);
	static Matrix< T, 4, 4 > transpose(const Matrix< T, 4, 4 >& m);
	static Matrix< T, 4, 4 > cofactor(const Matrix< T, 4, 4 >& m);
	static Matrix< T, 4, 4 > adjoint(const Matrix< T, 4, 4 >& m);
	static Matrix< T, 4, 4 > inverse(const Matrix< T, 4, 4 >& m);
	static Matrix< T, 4, 4 > translation(const Vector< T, 3 >& v);

	static Matrix< T, 4, 4 > perspective(T fov, T aspectRatio, T zNear, T zFar);
	static Matrix< T, 4, 4 > view(const Matrix< T, 4, 4 >& transform);
};

typedef Matrix< dw::float32, 2, 2 > Matrix22f;
typedef Matrix< dw::float32, 3, 3 > Matrix33f;
typedef Matrix< dw::float32, 4, 4 > Matrix44f;
typedef Matrix< dw::float64, 2, 2 > Matrix22d;
typedef Matrix< dw::float64, 3, 3 > Matrix33d;
typedef Matrix< dw::float64, 4, 4 > Matrix44d;

typedef Matrix22f Matrix2f;
typedef Matrix33f Matrix3f;
typedef Matrix44f Matrix4f;
typedef Matrix22d Matrix2d;
typedef Matrix33d Matrix3d;
typedef Matrix44d Matrix4d;

typedef Matrix22f mat2;
typedef Matrix33f mat3;
typedef Matrix44f mat4;

	} // namespace maths
} // namespace dw

#include "maths/matrix.inl"

#endif // !DW_MATHS_MATRIX_H

